
//TODO:
#[cfg(test)]
mod test {
    use tokio::sync::oneshot;
    

    #[tokio::test(flavor = "multi_thread")]
    async fn select_two_shot(){
        let (tx1, rx1) = oneshot::channel();
        let (tx2, rx2) = oneshot::channel();


        tokio::spawn( async {
            let _ = tx1.send("one");
        });

        tokio::spawn( async {
            let _ = tx2.send("two");
        });


        tokio::select! {
            value = rx1 => {
                println!("rx1 complted first with: {:?}", value);
            }
            value = rx2 => {
                println!("rx2 completed first with: {:?}", value);
            }
        }
    }

    async fn some_operation() -> String {
        let mut r = String::new();
        for _ in 0..1000_000 {
            r.push('c');
        }
        //println!("{r}");
        r
    }
    #[tokio::test(flavor = "multi_thread")]
    async fn cancel(){
        let (mut tx1, rx1) = oneshot::channel();

        tokio::spawn(async {
            tokio::select! {
                value = some_operation() => {
                    //assert!(false);
                    let _ = tx1.send(value);
                }
                _ = tx1.closed() => {
                    assert!(false);
                    println!("task of some_operation is canceled");
                }
            }
        });

        let (tx2, rx2) = oneshot::channel();
        tokio::spawn(async {
            let _ = tx2.send("two");
        });

        tokio::select! {
            val = rx1 => {
                assert!(false);
                println!("rx1 completed first with {:?}", val);
            }
            val = rx2 => {
                println!("rx2 completed first with {:?}", val);
            }
        }
    }

    
    #[tokio::test(flavor = "multi_thread")]
    async fn future_impl() {
        use tokio::sync::oneshot;
        use std::future::Future;
        use std::pin::Pin;
        use std::task::{Context, Poll};

        struct MySelect {
            rx1: oneshot::Receiver<&'static str>,
            rx2: oneshot::Receiver<&'static str>,
        }

        impl Future for MySelect {
            type Output = ();
            fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
                if let Poll::Ready(val) = Pin::new(&mut self.rx1).poll(cx) {
                    println!("future_impl: rx1 completed first with {:?}", val);
                    return Poll::Ready(())
                }
                if let Poll::Ready(val) = Pin::new(&mut self.rx2).poll(cx) {
                    println!("future_impl:  rx2 completed first with {:?}", val);
                    return Poll::Ready(())
                }

                Poll::Pending
            }
        }

        let (tx1, rx1) = oneshot::channel();
        let (tx2, rx2) = oneshot::channel();

        tokio::spawn(async { 
            MySelect {rx1, rx2}.await; // future for wait
        });

        let _ = tx1.send("one");
        let _ = tx2.send("two");
    }

    // syntax:  <pattern> = <async expression> => <handler>,
    use tokio::net::TcpStream; 
    #[tokio::test(flavor = "multi_thread")]
    async fn tcp_stream_connect() {
        let (tx, rx) = oneshot::channel();

        tokio::spawn( async move {
            tx.send("done").unwrap();
        });
        
        tokio::select! {
            socket = TcpStream::connect("127.0.0.1:22") => {
                println!("socket connected: {:?}", socket);
            }
            msg = rx => {
                println!("receiver message first {:?}", msg);
            }
        }
    }

    use tokio::net::TcpListener;
    use std::io;
    #[tokio::test(flavor = "multi_thread")]
    async fn tcp_listen_accept() -> io::Result<()>{

        fn process(socket: TcpStream){
            println!("process: socket: {:?}", socket);
        }

        let (tx, rx) = oneshot::channel();
        tokio::spawn(async move {
            tx.send(()).unwrap();
        });
        
        let listener = TcpListener::bind("127.0.0.1:3464").await?;
        tokio::select! {
            _ = async {
                loop {
                    let (socket, _) = listener.accept().await?;
                    tokio::spawn(async move { process(socket)});
                    assert!(false);
                    break;
                }
                Ok::<_, io::Error>(())
 
            } => {}
            _ = rx => {
                println!("terminating accept loop");
            }
        }
        Ok(())
    }
    
    #[tokio::test(flavor = "multi_thread")]
    async fn return_value() {
        async fn computation1() -> String {
            "One".to_string()
        }
        async fn computation2() -> String {
            "Two".to_string()
        }

        let out = tokio::select! {
            res1 = computation1() => res1,
            res2 = computation2() => res2,
        };
        assert!(out == "One" || out == "Two");
    }
    
    #[tokio::test(flavor = "multi_thread")]
    async fn more_pattern() {
        use tokio::sync::mpsc;
        let (tx1, mut rx1) = mpsc::channel(16);
        let (tx2, mut rx2) = mpsc::channel(16);

        tokio::spawn(async move {
            let _ = tx1.send("Hello");
            let _ = tx2.send("World");
        });

        //pattern => Some(x), Ok(x), Err(..)....
        tokio::select! {
            Some(v) = rx1.recv() => {
                println!("Got {:?} from rx1", v);
            }
            Some(v) = rx2.recv() => {
                println!("Got {:?} from rx2", v);
            }
            else => {
                println!("Both channels closed");
            }
        }
    }

    
    #[tokio::test(flavor = "multi_thread")]
    async fn borrowing() {
        let (tx1, rx1) = oneshot::channel();
        let (tx2, rx2) = oneshot::channel();
        let mut out = String::new();

        tokio::spawn( async move {
            let _ = tx1.send("one");
            let _ = tx2.send("two");
        });


        tokio::select! {
            val = rx1 => {
                out.push_str(val.unwrap());
            }
            val = rx2 => {
                out.push_str(val.unwrap());
            }
        }
        assert!(out == "one" || out == "two");
    }
}
